package com.tool.grab.vpn.task;

import android.net.VpnService;

import com.tool.grab.Logs;
import com.tool.grab.vpn.listener.DeleteEvent;
import com.tool.grab.vpn.tunnel.TcpTunnel;
import com.tool.grab.vpn.tunnel.TunnelInterface;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

public class TcpService implements Runnable,DeleteEvent {
    private final static String TAG = TcpService.class.getSimpleName();

    private ServerSocketChannel channel;
    private Selector selector;

    private boolean isRun;
    private int port;
    private VpnService service;

    private List<TunnelInterface> interfaces;

    public TcpService(VpnService service) throws IOException{
        this.service = service;
        selector = Selector.open();
        channel = ServerSocketChannel.open();
        channel.configureBlocking(false);
        channel.socket().bind(new InetSocketAddress(0));
        channel.register(selector,SelectionKey.OP_ACCEPT,this);
        this.port = channel.socket().getLocalPort();
    }

    public void start(){
        Thread thread = new Thread(this,"TCPService");
        thread.start();
    }

    @Override
    public void run() {
        try {
            isRun = true;
            while (isRun){
                int select = selector.select();

                if(select == 0){
                    Thread.sleep(20);
                    continue;
                }

                Set<SelectionKey> keys = selector.selectedKeys();

                if(keys == null) continue;

                Iterator<SelectionKey> keyIterator = selector.selectedKeys().iterator();

                while (keyIterator.hasNext()){
                    SelectionKey key = keyIterator.next();
                    if(key.isValid()){
                        if(key.isAcceptable()){
                            if(interfaces == null) interfaces = new ArrayList<>();

                            TunnelInterface tunnelInterface = new TcpTunnel(channel.accept(),selector,this);
                            tunnelInterface.connect(service);
                            interfaces.add(tunnelInterface);
                        }else{
                            Object object = key.attachment();
                            if(object instanceof TunnelInterface){
                                ((TunnelInterface) object).keyReady(key);
                            }
                        }
                    }
                    keyIterator.remove();
                }
            }
        }catch (Exception e){
            Logs.i(TAG,e.toString());
            stop();
        }
    }

    public void close(){
        isRun = false;
    }


    public void stop(){
        isRun = false;
        if(channel != null){
            try {
                channel.close();
            }catch (IOException e1){}finally {
                channel = null;
            }
        }

        if(selector != null){
            try {
                selector.close();
            }catch (IOException e1){}finally {
                selector = null;
            }
        }
    }

    public int getPort() {
        return port;
    }

    public boolean isRun() {
        return isRun;
    }

    @Override
    public void onDelete(TunnelInterface tunnel) {
        if(interfaces != null){
            interfaces.remove(tunnel);
        }
    }
}
